#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#       player.py
#
#       Copyright (C) 2010  Catullus Cassius
#
#       This file is part of python-vkontakte-player.
#
#       This program is free software: you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation, either version 3 of the License, or
#       (at your option) any later version.
#
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#
#       You should have received a copy of the GNU General Public License
#       along with this program.  If not, see <http://www.gnu.org/licenses/>.

import random, sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4.phonon import Phonon

displayed = ['artist', 'title', 'duration']

def secToHMS(seconds):
	seconds = int(seconds)
	hours = seconds / 3600
	seconds -= 3600*hours
	minutes = seconds / 60
	seconds -= 60*minutes
	if hours == 0:
		return "%02d:%02d" % (minutes, seconds)
	return "%02d:%02d:%02d" % (hours, minutes, seconds)


class playlistModel(QtCore.QAbstractTableModel):
	def __init__(self, playlist=[]):
		QtCore.QAbstractListModel.__init__(self)
		self.playlist = playlist
		self.highlightedRow = -1

	def rowCount(self, parent=None):
		return len(self.playlist)

	def columnCount(self, parent=None):
		return len(displayed)

	def data(self, index, role = None):
		if not index.isValid():
			return QtCore.QVariant()
		if role == QtCore.Qt.DisplayRole:
			if 'duration' in displayed and index.column() == displayed.index('duration'):
				return QtCore.QString(secToHMS(self.playlist[index.row()][displayed[index.column()]]))
			return QtCore.QString(self.playlist[index.row()][displayed[index.column()]])
		if role == QtCore.Qt.BackgroundRole and index.row() == self.highlightedRow:
			return QtGui.QColor("yellow")

	def headerData(self, section, orientation, role = QtCore.Qt.DisplayRole):
		if role != QtCore.Qt.DisplayRole:
			return QtCore.QVariant()
		if orientation == QtCore.Qt.Horizontal:
			return QtCore.QString(displayed[section])
		else:
			return QtCore.QString(unicode(section+1))

	def insertRows(self, position, rows, parent = None):
		self.beginInsertRows(QtCore.QModelIndex(), position, position+rows-1)
		if self.highlightedRow > position:
			self.highlightedRow += rows
		empty_line = {}
		for column_name in displayed:
			empty_line[column_name] = None
		for row in range(rows):
			self.playlist.insert(position, empty_line)
		self.endInsertRows()
		return True

	def setData(self, index, value, role = QtCore.Qt.EditRole):
		if index.isValid() and role == QtCore.Qt.EditRole:
			self.playlist[index.row()] = value
			self.emit(QtCore.SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
				self.index(index.row(), 0),
				self.index(index.row(), self.columnCount()))
			return True
		return False

	def removeRows(self, position, rows, parent = None):
		self.beginRemoveRows(QtCore.QModelIndex(), position, position+rows-1)
		if self.highlightedRow >= position and self.highlightedRow <= position + rows -1:
			self.highlightedRow = -1
		elif self.highlightedRow > position:
			self.highlightedRow -= rows
		for row in range(rows):
			self.playlist.pop(position)
		self.endRemoveRows()
		return True

	def highlighted(self):
		return self.highlightedRow

	def setHighlighted(self, highlightedRow):
		if highlightedRow < self.rowCount():
			if self.highlightedRow >= 0:
				self.emit(QtCore.SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
					self.index(self.highlightedRow, 0),
					self.index(self.highlightedRow, self.columnCount()))
			self.highlightedRow = highlightedRow
			self.emit(QtCore.SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
				self.index(self.highlightedRow, 0),
				self.index(self.highlightedRow, self.columnCount()))

#Should I use QObject as the base class?
class player(QtCore.QObject):
	def __init__(self):
		QtCore.QObject.__init__(self)
		self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
		self.metaInformationResolver = Phonon.MediaObject(self)
		self.mediaObject = Phonon.MediaObject(self)
		#self.mediaObject.setTickInterval(1000)
		Phonon.createPath(self.mediaObject, self.audioOutput)

		self.connect(self.mediaObject, QtCore.SIGNAL("finished()"), self.next)
		#self.connect(self.mediaObject, QtCore.SIGNAL("aboutToFinish()"), self.enqueue)

		self.playlist_model = playlistModel()
		self.state = "stop"
		self.mode = "simple"
		self.consume = False
		self.single = False
		self.position = -1
		self.next_cached = -1
		self.prev_cached = -1

	def add(self, link):
		self.playlist_model.insertRows(self.playlist_model.rowCount(), 1, None)
		new_row = self.playlist_model.rowCount()-1
		index = self.playlist_model.index(new_row, 0)
		self.playlist_model.setData(index, link)
		return "OK"

	def delete(self, pos):
		pos.sort()
		if self.position in pos:
			self.stop()
			self.position = -1
		else:
			#pos is sorted, should use it, but dont...
			shift = 0
			shift_next_cached = 0
			shift_prev_cached = 0
			for p in pos:
				if self.position > p:
					shift += 1
				if self.next_cached > p:
					shift_next_cached += 1
				if self.prev_cached > p:
					shift_prev_cached += 1
			self.position -= shift
			self.next_cached -= shift_next_cached
			self.prev_cached -= shift_prev_cached
		pos.reverse()
		for i in pos:
			self.playlist_model.removeRows(i, 1)
		if self.next_cached in pos:
			self.update_next_cached()
		if self.prev_cached in pos:
			self.update_prev_cached()

	def get_bitrate(self, pos):
		if not 'bitrate' in displayed:
			return
		current_pos = self.position
		#should use another dummy mediaObject
		self.mediaObject.setCurrentSource(Phonon.MediaSource(QtCore.QUrl(self.playlist_model.playlist[pos]['url'])))
		bitrate_list = self.mediaObject.metaData('BITRATE')
		index = self.playlist_model.index(pos, displayed.index('bitrate'))
		if len(bitrate_list) > 0:
			#should use setData
			self.playlist_model.playlist[pos]['bitrate']=bitrate_list[0]
		else:
			#should use setData
			self.playlist_model.playlist[pos]['bitrate']="none"
		#should use another dummy mediaObject
		self.mediaObject.setCurrentSource(Phonon.MediaSource(QtCore.QUrl(self.playlist_model.playlist[current_pos]['url'])))

	def play(self):
		if self.playlist_model.rowCount() == 0:
			return
		if self.position == -1:
			self.position = self.get_first()
		if self.state == "pause":
			self.mediaObject.play()
			return
		self.mediaObject.setCurrentSource(Phonon.MediaSource(QtCore.QUrl(self.playlist_model.playlist[self.position]['url'])))
		self.mediaObject.play()
		self.playlist_model.setHighlighted(self.position)
		self.update_cached()
		self.state = "play"
		return "OK"

	def play_pause(self):
		if self.state == "play":
			self.pause()
		else:
			self.play()

	def stop(self):
		if self.state != "stop":
			self.mediaObject.stop()
			self.state = "stop"
		return "OK"

	def next(self):
		if self.position != -1 and self.state != "stop":
			if self.consume:
				self.playlist_model.removeRows(self.position, 1)
			if self.consume and self.next_cached > self.position:
				self.position = self.next_cached - 1
			else:
				self.position = self.next_cached
			#self.choose_next()
			if self.position != -1:
				self.play()
			else:
				self.stop()
				self.playlist_model.setHighlighted(self.position)
		return "OK"

	def prev(self):
		if self.position != -1 and self.state != "stop":
			if self.consume:
				self.playlist_model.removeRows(self.position, 1)
			if self.consume and self.prev_cached > self.position:
				self.position = self.prev_cached - 1
			else:
				self.position = self.prev_cached
			#self.choose_prev()
			if self.position != -1:
				self.play()
			else:
				self.stop()
				self.playlist_model.setHighlighted(self.position)
		return "OK"

	def pause(self):
		if self.state == "play":
			self.mediaObject.pause()
			self.state = "pause"
		return "OK"

	def update_next_cached(self):
		if self.position != -1:
			self.next_cached = self.get_next()
			#self.enqueue()

	def update_prev_cached(self):
		if self.position != -1:
			self.prev_cached = self.get_prev()

	def update_cached(self):
		self.update_prev_cached()
		self.update_next_cached()

	def enqueue(self):
		if self.next_cached != -1:
			self.mediaObject.enqueue([Phonon.MediaSource(QtCore.QUrl(self.playlist_model.playlist[self.next_cached]['url']))])
			print self.playlist_model.playlist[self.next_cached]['title']

	def get_first(self):
		if self.mode == "random":
			return random.randint(0, self.playlist_model.rowCount()-1)
		else:
			return 0

	def get_next(self):
		if self.playlist_model.rowCount() == 0:
			return -1
		if self.position == -1:
			return self.get_first()
		if self.mode == "simple" and ((not self.consume and not self.single) or self.consume):
			if self.position == self.playlist_model.rowCount() - 1:
				return -1
			else:
				return self.position + 1
		elif self.mode == "simple" and not self.consume and self.single:
			return -1
		elif self.mode == "repeat" and not self.consume and not self.single:
			return (self.position + 1) % self.playlist_model.rowCount()
		elif self.mode == "repeat" and not self.consume and self.single:
			return self.position
		elif self.mode == "repeat" and self.consume:
			if self.playlist_model.rowCount() == 1:
				return -1
			elif self.position == self.playlist_model.rowCount() - 1:
				return 0
			else:
				return self.position + 1
		elif self.mode == "random" and not self.consume:
			return random.randint(0, self.playlist_model.rowCount()-1)
		elif self.mode == "random" and self.consume:
			if self.playlist_model.rowCount() == 1:
				return -1
			else:
				position = random.randint(0, self.playlist_model.rowCount()-2)
				if position >= self.position:
					return position - 1
				else:
					return position

	def get_prev(self):
		if self.playlist_model.rowCount() == 0:
			return -1
		if self.position == -1:
			return self.get_first()
		if self.mode == "simple" and ((not self.consume and not self.single) or self.consume):
			if self.position == 0:
				return -1
			else:
				return self.position - 1
		elif self.mode == "simple" and not self.consume and self.single:
			return -1
		elif self.mode == "repeat" and not self.consume and not self.single:
			return (self.playlist_model.rowCount() + self.position - 1) % self.playlist_model.rowCount()
		elif self.mode == "repeat" and not self.consume and self.single:
			return self.position
		elif self.mode == "repeat" and self.consume:
			if self.playlist_model.rowCount() == 1:
				return -1
			if self.position == 0:
				return self.playlist_model.rowCount() - 1
			else:
				self.position = self.position - 1
		elif self.mode == "random" and not self.consume:
			return random.randint(0, self.playlist_model.rowCount()-1)
		elif self.mode == "random" and self.consume:
			if self.playlist_model.rowCount() == 1:
				return -1
			else:
				position = random.randint(0, self.playlist_model.rowCount()-2)
				if position >= self.position:
					return position - 1
				else:
					return position

